/*
 * Copyright 2016 jeasonlzy(廖子尧)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lzy.okgo.db;

import com.lzy.okgo.utils.OkLogger;
import ohos.data.rdb.RawRdbPredicates;
import ohos.data.rdb.RdbStore;
import ohos.data.rdb.ValuesBucket;
import ohos.data.resultset.ResultSet;
import ohos.data.resultset.ResultSetHook;
import ohos.utils.Pair;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.locks.Lock;

/**
 * ================================================
 * 作    者：jeasonlzy（廖子尧）
 * 版    本：1.0
 * 创建日期：16/9/11
 * 描    述：
 * 修订历史：
 * ================================================
 */
public abstract class BaseDao<T> {

    private static String TAG;
    private Lock lock;
    private RdbStore database;

    public BaseDao(RdbStore database) {
        TAG = getClass().getSimpleName();
        lock = DBHelper.lock;
        this.database = database;
    }

    protected final void closeDatabase(RdbStore database, ResultSet cursor) {
        if (cursor != null && !cursor.isClosed()) cursor.close();
        if (database != null && database.isOpen()) database.close();
    }

    public boolean insert(T t) {
        if (t == null) return false;
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            database.insert(getTableName(), getContentValues(t));
            database.markAsCommit();
            return true;
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        } finally {
            database.endTransaction();
            lock.unlock();
            OkLogger.v(TAG, System.currentTimeMillis() - start + " insertT");
        }
        return false;
    }

    public long insert(RdbStore database, T t) {
        return database.insert(getTableName(), getContentValues(t));
    }

    public boolean insert(List<T> ts) {
        if (ts == null) return false;
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            for (T t : ts) {
                database.insert(getTableName(), getContentValues(t));
            }
            database.markAsCommit();
            return true;
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        } finally {
            database.endTransaction();
            lock.unlock();
            OkLogger.v(TAG, System.currentTimeMillis() - start + " insertList");
        }
        return false;
    }

    public boolean insert(RdbStore database, List<T> ts) {
        try {
            for (T t : ts) {
                database.insert(getTableName(), getContentValues(t));
            }
            return true;
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
            return false;
        }
    }

    public boolean deleteAll() {
        return delete(null, null);
    }

    public int deleteAll(RdbStore database) {
        return delete(database, null, null);
    }

    public boolean delete(String whereClause, String[] whereArgs) {
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            RawRdbPredicates rdbPredicates = new RawRdbPredicates(getTableName());
            rdbPredicates.setWhereClause(whereClause);
            if (whereArgs == null || whereArgs.length == 0) {
                rdbPredicates.setWhereArgs(null);
            } else {
                rdbPredicates.setWhereArgs(Arrays.asList(whereArgs));
            }
            database.delete(rdbPredicates);
            database.markAsCommit();
            return true;
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        } finally {
            database.endTransaction();
            lock.unlock();
            OkLogger.v(TAG, System.currentTimeMillis() - start + " delete");
        }
        return false;
    }

    public int delete(RdbStore database, String whereClause, String[] whereArgs) {
        RawRdbPredicates rawRdbPredicates = new RawRdbPredicates(getTableName());
        rawRdbPredicates.setWhereClause(whereClause);
        if (whereArgs == null || whereArgs.length == 0) {
            rawRdbPredicates.setWhereArgs(null);
        } else {
            rawRdbPredicates.setWhereArgs(Arrays.asList(whereArgs));
        }
        return database.delete(rawRdbPredicates);
    }

    public boolean deleteList(List<Pair<String, String[]>> where) {
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            for (Pair<String, String[]> pair : where) {
                RawRdbPredicates rawRdbPredicates = new RawRdbPredicates(getTableName());
                rawRdbPredicates.setWhereClause(pair.f);
                if (pair.s == null || pair.s.length == 0) {
                    rawRdbPredicates.setWhereArgs(null);
                } else {
                    rawRdbPredicates.setWhereArgs(Arrays.asList(pair.s));
                }
                database.delete(rawRdbPredicates);
            }
            database.markAsCommit();
            return true;
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        } finally {
            database.endTransaction();
            lock.unlock();
            OkLogger.v(TAG, System.currentTimeMillis() - start + " deleteList");
        }
        return false;
    }

    public boolean replace(T t) {
        if (t == null) return false;
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            database.replace(getTableName(), getContentValues(t));
            database.markAsCommit();
            return true;
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        } finally {
            database.endTransaction();
            lock.unlock();
            OkLogger.v(TAG, System.currentTimeMillis() - start + " replaceT");
        }
        return false;
    }

    public long replace(RdbStore database, T t) {
        return database.replace(getTableName(), getContentValues(t));
    }

    public boolean replace(ValuesBucket contentValues) {
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            database.replace(getTableName(), contentValues);
            database.markAsCommit();
            return true;
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        } finally {
            database.endTransaction();
            lock.unlock();
            OkLogger.v(TAG, System.currentTimeMillis() - start + " replaceContentValues");
        }
        return false;
    }

    public long replace(RdbStore database, ValuesBucket contentValues) {
        return database.replace(getTableName(), contentValues);
    }

    public boolean replace(List<T> ts) {
        if (ts == null) return false;
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            for (T t : ts) {
                database.replace(getTableName(), getContentValues(t));
            }
            database.markAsCommit();
            return true;
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        } finally {
            database.endTransaction();
            lock.unlock();
            OkLogger.v(TAG, System.currentTimeMillis() - start + " replaceList");
        }
        return false;
    }

    public boolean replace(RdbStore database, List<T> ts) {
        try {
            for (T t : ts) {
                database.replace(getTableName(), getContentValues(t));
            }
            return true;
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
            return false;
        }
    }

    public boolean update(T t, String whereClause, String[] whereArgs) {
        if (t == null) return false;
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            RawRdbPredicates rawRdbPredicates = new RawRdbPredicates(getTableName());
            rawRdbPredicates.setWhereClause(whereClause);
            if (whereArgs == null || whereArgs.length == 0) {
                rawRdbPredicates.setWhereArgs(null);
            } else {
                rawRdbPredicates.setWhereArgs(Arrays.asList(whereArgs));
            }
            database.update(getContentValues(t), rawRdbPredicates);
            database.markAsCommit();
            return true;
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        } finally {
            database.endTransaction();
            lock.unlock();
            OkLogger.v(TAG, System.currentTimeMillis() - start + " updateT");
        }
        return false;
    }

    public long update(RdbStore database, T t, String whereClause, String[] whereArgs) {
        RawRdbPredicates rawRdbPredicates = new RawRdbPredicates(getTableName());
        rawRdbPredicates.setWhereClause(whereClause);
        if (whereArgs == null || whereArgs.length == 0) {
            rawRdbPredicates.setWhereArgs(null);
        } else {
            rawRdbPredicates.setWhereArgs(Arrays.asList(whereArgs));
        }
        return database.update(getContentValues(t), rawRdbPredicates);
    }

    public boolean update(ValuesBucket contentValues, String whereClause, String[] whereArgs) {
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            RawRdbPredicates rawRdbPredicates = new RawRdbPredicates(getTableName());
            rawRdbPredicates.setWhereClause(whereClause);
            if (whereArgs == null || whereArgs.length == 0) {
                rawRdbPredicates.setWhereArgs(null);
            } else {
                rawRdbPredicates.setWhereArgs(Arrays.asList(whereArgs));
            }
            database.update(contentValues, rawRdbPredicates);
            database.markAsCommit();
            return true;
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        } finally {
            database.endTransaction();
            lock.unlock();
            OkLogger.v(TAG, System.currentTimeMillis() - start + " updateContentValues");
        }
        return false;
    }

    public long update(RdbStore database, ValuesBucket contentValues, String whereClause, String[] whereArgs) {
        RawRdbPredicates rawRdbPredicates = new RawRdbPredicates(getTableName());
        rawRdbPredicates.setWhereClause(whereClause);
        if (whereArgs == null || whereArgs.length == 0) {
            rawRdbPredicates.setWhereArgs(null);
        } else {
            rawRdbPredicates.setWhereArgs(Arrays.asList(whereArgs));
        }
        return database.update(contentValues, rawRdbPredicates);
    }

    public List<T> queryAll(RdbStore database) {
        return query(database, null, null);
    }

    public List<T> query(RdbStore database, String selection, String[] selectionArgs) {
        return query(database, null, selection, selectionArgs, null, null, null, null);
    }

    public T queryOne(RdbStore database, String selection, String[] selectionArgs) {
        List<T> query = query(database, null, selection, selectionArgs, null, null, null, "1");
        if (query.size() > 0) return query.get(0);
        return null;
    }

    public List<T> query(RdbStore database, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) {
        List<T> list = new ArrayList<>();
        ResultSet cursor = null;
        try {
            RawRdbPredicates rawRdbPredicates = new RawRdbPredicates(getTableName());
            rawRdbPredicates.setWhereClause(selection);
            if (selectionArgs == null || selectionArgs.length == 0) {
                rawRdbPredicates.setWhereArgs(null);
            } else {
                rawRdbPredicates.setWhereArgs(Arrays.asList(selectionArgs));
            }
            cursor = database.query(rawRdbPredicates, columns);
            while (!cursor.isClosed() && cursor.goToNextRow()) {
                list.add(parseCursorToBean(cursor));
            }
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        } finally {
            closeDatabase(database, cursor);
        }
        return list;
    }

    public List<T> queryAll() {
        return query(null, null);
    }

    public List<T> query(String selection, String[] selectionArgs) {
        return query(null, selection, selectionArgs, null, null, null, null);
    }

    public T queryOne(String selection, String[] selectionArgs) {
        long start = System.currentTimeMillis();
        List<T> query = query(null, selection, selectionArgs, null, null, null, "1");
        OkLogger.e(TAG, System.currentTimeMillis() - start + " queryOne");
        return query.size() > 0 ? query.get(0) : null;
    }

    public List<T> query(String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) {
        long start = System.currentTimeMillis();
        lock.lock();
        List<T> list = new ArrayList<>();
        ResultSet cursor = null;
        try {
            database.beginTransaction();
//            RdbPredicates rdbPredicates = new RdbPredicates(getTableName());
//            if (!TextUtils.isEmpty(selection)) {
//                rdbPredicates.in(selection, selectionArgs);
//            }
//            if (!TextUtils.isEmpty(groupBy)) {
//                rdbPredicates.groupBy(new String[]{groupBy});
//            }
//            if (!TextUtils.isEmpty(orderBy)) {
//                rdbPredicates.orderByAsc(orderBy);
//            }
//            if (!TextUtils.isEmpty(limit)) {
//                rdbPredicates.limit(Integer.parseInt(limit));
//            }
            RawRdbPredicates rawRdbPredicates = new RawRdbPredicates(getTableName());
            rawRdbPredicates.setWhereClause(selection);
            if (selectionArgs == null || selectionArgs.length == 0) {
                rawRdbPredicates.setWhereArgs(null);
            } else {
                rawRdbPredicates.setWhereArgs(Arrays.asList(selectionArgs));
            }
            cursor = database.query(rawRdbPredicates, columns);
            while (!cursor.isClosed() && cursor.goToNextRow()) {
                list.add(parseCursorToBean(cursor));
            }
            database.markAsCommit();
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        } finally {
            closeDatabase(null, cursor);
            database.endTransaction();
            lock.unlock();
            OkLogger.v(TAG, System.currentTimeMillis() - start + " query");
        }
        return list;
    }

    public interface Action {
        void call(RdbStore database);
    }

    public void startTransaction(Action action) {
        lock.lock();
        try {
            database.beginTransaction();
            action.call(database);
            database.markAsCommit();
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        } finally {
            database.endTransaction();
            lock.unlock();
        }
    }

    public abstract String getTableName();

    public abstract void unInit();

    public abstract T parseCursorToBean(ResultSet cursor);

    public abstract ValuesBucket getContentValues(T t);
}
